/**
 * Copyright (C) 2013 Denatics Development
 *
 * Programmed by David Tiersch <dtiersch@denatics.com>
 *
 * This file is part of B0mberman.
 *
 * B0mberman is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * B0mberman is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with B0mberman. If not, see <http://www.gnu.org/licenses/>.
 */
package com.denatics.bomberman.tools;

import java.util.ArrayList;
import java.util.List;

import com.denatics.bomberman.Config;
import com.denatics.bomberman.tools.Debugger.LEVEL;

/**
 * @author David Tiersch <dtiersch@denatics.com>
 *
 */
public final class Timer implements Runnable {

    /**
     * Needed to convert between seconds and milliseconds.
     */
    private static final int MILLISEC = 1000000000;

    /**
     * Current Status of the Thread.
     */
    private boolean running;

    /**
     * Thread.
     */
    private Thread thread;

    /**
     * List of the classes who listens for a tick.
     */
    private List<Tickable> ticker;

    /**
     * Creates the Timer.
     */
    public Timer() {
        this.running = false;
        this.ticker = new ArrayList<Tickable>();
    }

    /**
     * Starts the thread.
     */
    public synchronized void start() {
        if (this.running) {
            return;
        }
        Debugger.printInfo(LEVEL.STARTUP, "Timer started");
        this.running = true;
        this.thread = new Thread(this);
        this.thread.start();
    }

    /**
     * Stops the thread.
     */
    public synchronized void stop() {
        if (!this.running) {
            return;
        }
        Debugger.printInfo(LEVEL.STARTUP, "Timer stopped");
        this.running = false;
        try {
            this.thread.join();
        } catch (InterruptedException e) {
            Debugger.printError(e.getMessage());
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        double idledTime = 0.0;
        long lastTime = System.nanoTime();
        final double secondsPerTick = 1 / (double) Config.TICKS;

        while (this.running) {
            final long now = System.nanoTime();
            long passedTime = now - lastTime;
            lastTime = now;
            if (passedTime < 0) {
                passedTime = 0;
            }
            if (passedTime > MILLISEC) {
                passedTime = MILLISEC;
            }

            idledTime += passedTime / (double) MILLISEC;

            if (idledTime > secondsPerTick) {
                this.tick();
                idledTime = 0.0;
            } else {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    Debugger.printError(e.getMessage());
                }
            }
        }
    }

    /**
     * Sends to all needed classes a tick.
     */
    private void tick() {
        for (int i = 0; i < this.ticker.size(); i++) {
            this.ticker.get(i).tick();
        }
    }

    /**
     * Adds a Listener to the List who gets ticked.
     *
     * @param listener
     *            new Listener to the tick
     */
    public void addListener(final Tickable listener) {
        this.ticker.add(listener);
    }

}
